package principal;

import java.awt.Component;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class ReconocedorFormasPrincipales implements IReconocedorFormas {

	public LinkedList<Calle> calles;
	public LinkedList<Forma> formas;
	public LinkedList<Linea> lineas;
	private Trazo t;

	public ReconocedorFormasPrincipales(LinkedList<Calle> c, LinkedList<Forma> f, LinkedList<Linea>l){
		calles = c;
		formas = f;
		lineas = l;
	}
	
	public void setTrazo(Trazo t) {
		this.t = t;
	}

	public int reconoceTrazo() {
		
		// Descartar que sea una recta, que no va de una forma a otra
		if(reconoceRecta())
			return 5;
		
		if(reconoceCalle())
			return 6;
		
		Calle calleForma =  getCalle();
		
		Point centro = t.getCentroMasa();

		Recta vert = new Recta(1, 0, -(int) centro.getX());
		Recta hori = new Recta(0, 1, -(int) centro.getY());

		Recta diag1 = new Recta(1, -1, -centro.x + centro.y);
		Recta diag2 = new Recta(1, 1, -centro.x - centro.y);

		List puntos_arriba = t.getPuntosArriba(centro);
		List puntos_abajo = t.getPuntosAbajo(centro);
		List puntos_derecha = t.getPuntosDerecha(centro);
		List puntos_izquierda = t.getPuntosIzquierda(centro);

		Point2D punto1 = obtenerCorte(vert, puntos_arriba);
		double distancia1 = distancia(centro, punto1);

		Point2D punto2 = obtenerCorte(diag1, puntos_arriba);
		double distancia2 = distancia(centro, punto2);

		Point2D punto3 = obtenerCorte(hori, puntos_derecha);
		double distancia3 = distancia(centro, punto3);

		Point2D punto4 = obtenerCorte(diag2, puntos_abajo);
		double distancia4 = distancia(centro, punto4);

		Point2D punto5 = obtenerCorte(vert, puntos_abajo);
		double distancia5 = distancia(centro, punto5);

		Point2D punto6 = obtenerCorte(diag1, puntos_abajo);
		double distancia6 = distancia(centro, punto6);

		Point2D punto7 = obtenerCorte(hori, puntos_izquierda);
		double distancia7 = distancia(centro, punto7);

		Point2D punto8 = obtenerCorte(diag2, puntos_arriba);
		double distancia8 = distancia(centro, punto8);

		double distancia_maxima = maximo(distancia1, distancia2, distancia3,
				distancia4, distancia5, distancia6, distancia7, distancia8);

		// normalizamos las distancias y las guardamos en un vector:

		double[] distancias = new double[8];

		distancias[0] = distancia1 / distancia_maxima;
		distancias[1] = distancia2 / distancia_maxima;
		distancias[2] = distancia3 / distancia_maxima;
		distancias[3] = distancia4 / distancia_maxima;
		distancias[4] = distancia5 / distancia_maxima;
		distancias[5] = distancia6 / distancia_maxima;
		distancias[6] = distancia7 / distancia_maxima;
		distancias[7] = distancia8 / distancia_maxima;

		double sim_documento = similitud_documento(distancias);
		double sim_actividad = similitud_actividad(distancias);
		double sim_evento = similitud_evento(distancias);
		double sim_nexo = similitud_nexo(distancias);

		double valor_opcion = sim_actividad;
		int ret = 1;// 1, codigo para actividad

		if (valor_opcion > sim_documento) {
			valor_opcion = sim_documento;
			ret = 2;// 2, codigo para documento
		}

		if (valor_opcion > sim_evento) {
			valor_opcion = sim_evento;
			ret = 3;// 3, codigo para evento
		}

		if (valor_opcion > sim_nexo) {
			valor_opcion = sim_nexo;
			ret = 4;// 4, codigo para nexo
		}
		
		if(ret != 2)
			if(calleForma != null)
				return ret;
			else
				return -1;
		else
			return ret;
		
	}

	private boolean reconoceCalle() {
		if(t.getAchura()>800)
    		return true;
		else
			return false;
    			
	}

	private boolean reconoceRecta() {
		
		Forma or = null;
    	Forma dest = null;
    	or = esta_en_una_forma(t.getOrigen());
    	dest = esta_en_una_forma(t.getFin());
    	if(or!=null && dest != null)
    		return true;
    	if(or==null && dest!=null && dest.getTipo()==2 ||
    			dest==null && or!=null && or.getTipo()==2){//Solo tiene un extremo, que es un documento
    		Point2D p;
    		Forma f;
    		if(or == null){
    			p = t.getOrigen();
    			f = dest;
    		}
    		else{
    			p = t.getFin();
    			f = or;
    		}    		
    		//Itero sobre las lineas a ver si hay una que este cerca del extremo del trazo pintado
    		Iterator it = lineas.iterator();
    		Linea l_tmp;
    		Recta r_tmp;
    		boolean enc = false;
    		while(it.hasNext() && !enc){
    			l_tmp = (Linea)it.next();
    			r_tmp = new Recta(l_tmp);
    			if(r_tmp.distancia(p)<10){
    				return true;
    			}
    		}
    	}
		return false;
	}

	private double similitud_nexo(double[] distancias) {
		double[] diferencias = new double[8];
		diferencias[0] = Math.abs(distancias[0] - 1);
		diferencias[1] = Math.abs(distancias[1] - 0.7);
		diferencias[2] = Math.abs(distancias[2] - 1);
		diferencias[3] = Math.abs(distancias[3] - 0.7);
		diferencias[4] = Math.abs(distancias[4] - 1);
		diferencias[5] = Math.abs(distancias[5] - 0.7);
		diferencias[6] = Math.abs(distancias[6] - 1);
		diferencias[7] = Math.abs(distancias[7] - 0.7);

		return media_distancias(diferencias);
	}

	private double similitud_evento(double[] distancias) {
		// TODO Auto-generated method stub
		double[] diferencias = new double[8];
		diferencias[0] = Math.abs(distancias[0] - 1);
		diferencias[1] = Math.abs(distancias[1] - 1);
		diferencias[2] = Math.abs(distancias[2] - 1);
		diferencias[3] = Math.abs(distancias[3] - 1);
		diferencias[4] = Math.abs(distancias[4] - 1);
		diferencias[5] = Math.abs(distancias[5] - 1);
		diferencias[6] = Math.abs(distancias[6] - 1);
		diferencias[7] = Math.abs(distancias[7] - 1);

		return media_distancias(diferencias);
	}

	private double media_distancias(double[] distancias) {
		double suma = 0;
		for (int i = 0; i < distancias.length; i++)
			suma += distancias[i];
		return suma / distancias.length;
	}

	private double similitud_actividad(double[] distancias) {
		// TODO Auto-generated method stub
		double[] diferencias = new double[8];
		diferencias[0] = Math.abs(distancias[0] - 0.5);
		diferencias[1] = Math.abs(distancias[1] - 0.7);
		diferencias[2] = Math.abs(distancias[2] - 1);
		diferencias[3] = Math.abs(distancias[3] - 0.7);
		diferencias[4] = Math.abs(distancias[4] - 0.5);
		diferencias[5] = Math.abs(distancias[5] - 0.7);
		diferencias[6] = Math.abs(distancias[6] - 1);
		diferencias[7] = Math.abs(distancias[7] - 0.7);

		return media_distancias(diferencias);
	}

	private double similitud_documento(double[] distancias) {
		// TODO Auto-generated method stub
		double[] diferencias = new double[8];
		diferencias[0] = Math.abs(distancias[0] - 1);
		diferencias[1] = Math.abs(distancias[1] - 0.7);
		diferencias[2] = Math.abs(distancias[2] - 0.5);
		diferencias[3] = Math.abs(distancias[3] - 0.7);
		diferencias[4] = Math.abs(distancias[4] - 1);
		diferencias[5] = Math.abs(distancias[5] - 0.7);
		diferencias[6] = Math.abs(distancias[6] - 0.5);
		diferencias[7] = Math.abs(distancias[7] - 0.7);

		return media_distancias(diferencias);
	}

	private double maximo(double distancia1, double distancia2,
			double distancia3, double distancia4, double distancia5,
			double distancia6, double distancia7, double distancia8) {
		// TODO Auto-generated method stub
		double max = distancia1;
		if (distancia2 > max)
			max = distancia2;
		if (distancia3 > max)
			max = distancia3;
		if (distancia4 > max)
			max = distancia4;
		if (distancia5 > max)
			max = distancia5;
		if (distancia6 > max)
			max = distancia6;
		if (distancia7 > max)
			max = distancia7;
		if (distancia8 > max)
			max = distancia8;

		return max;
	}

	private double distancia(Point centro, Point2D punto1) {
		double dx = Math.pow((centro.getX() - punto1.getX()), 2);
		double dy = Math.pow((centro.getY() - punto1.getY()), 2);
		double res = Math.sqrt(dx + dy);
		return res;
	}

	// Itero sobre la lista de puntos hasta encontrar uno que pertenezca a la
	// recta
	/*
	 * NOTA: La lista depuntos no es continua, busco el punto de menor dsitancia
	 * a la recta, suponiendo que este es el corte
	 */
	private Point2D obtenerCorte(Recta r, List<Point2D> puntos_arriba) {
		// TODO Auto-generated method stub
		double min_dist = Double.MAX_VALUE;
		Iterator<Point2D> it = puntos_arriba.iterator();
		Point2D tmp, ret = null;
		double dist_act;
		while (it.hasNext()) {
			tmp = (Point2D) it.next();
			dist_act = r.distancia(tmp);
			if (dist_act < min_dist) {
				min_dist = dist_act;
				ret = tmp;
			}
		}
		return ret;
	}

	@Override
	public String reconoceEscritura(Component c) {
		// TODO Auto-generated method stub
		return null;
	}

	private Forma esta_en_una_forma(Point2D origen) {
		Iterator<Forma> it = formas.iterator();
		Forma tmp = null;
		Forma ret = null;
		boolean enc = false;
		while(it.hasNext() && !enc){
			tmp = (Forma)it.next();
			if(tmp.getTipo()==1){
				if(origen.getX()-tmp.getPos().getX()<160 && origen.getY()-tmp.getPos().getY()<60 && 
						origen.getX()-tmp.getPos().getX()>0 && origen.getY()-tmp.getPos().getY()>0){
					enc = true;
					ret = tmp;
				}
			}
			if(tmp.getTipo()==2){
				if(origen.getX()-tmp.getPos().getX()<33 && origen.getY()-tmp.getPos().getY()<50&& 
						origen.getX()-tmp.getPos().getX()>0 && origen.getY()-tmp.getPos().getY()>0){
					enc = true;
					ret = tmp;
				}
			}
			
			if(tmp.getTipo()==7 || tmp.getTipo()==8 || tmp.getTipo()==5 || tmp.getTipo()==6 || tmp.getTipo()==9 || tmp.getTipo()==10){
				if(origen.getX()-tmp.getPos().getX()<40 && origen.getY()-tmp.getPos().getY()<40&& 
						origen.getX()-tmp.getPos().getX()>0 && origen.getY()-tmp.getPos().getY()>0){
					enc = true;
					ret = tmp;
				}
			}
		}
		return ret;
	}

	private Calle getCalle() {
		Iterator<Calle> it = calles.iterator();
		Calle tmp, ret = null;
		boolean enc = false;
		while(it.hasNext() && !enc){
			tmp = (Calle)it.next();
			if(tmp.contieneTrazo(t)){
				enc = true;
				ret = tmp;
			}
		}
	
		return ret;
	}

}
